/*

  7-8-10
  Robert Cowan
  SparkFun Electronics
  
  Main code file for Scavenger Box
  
  Uses GPS and RFID to assist in waypoint/checkpoint based scacenger hunt

  Uses NewSoftSerial for utilizing multiple communications ports at once. Allows for LCD, RFID, and GPS communication.
  
  Uses TinyGPS for positioning and distance calculation
  
*/ 

// Include NewSoftSerial, TinyGPS, and EEPROM libraries 
#include <NewSoftSerial.h>
#include <TinyGPS.h>
#include <EEPROM.h>


//Defines communication pins for GPS, LCD, and RFID
#define GPSRX 3
#define GPSTX 2
#define LCDTX 4
#define LCDRX 5
#define RFIDRX 6
#define RFIDTX 7
#define RESETPIN 8

//Pin for speaker and variable for RFID tag array
int pinSpeaker = 11;

//LED pin 13 for testing
int ledPin =  13;

//address to write to the EEPROM
int address = 0;
int clue = 0;
char val = 0;


//Sets baud rates for LCD, GPS, and RFID
#define GPSBAUD 4800
#define LCDBAUD 115200
#define RFIDBAUD 9600

//Define variables for distance calculation
#define MILES_PER_METER      0.00062137f
#define EARTH_RADIUS_METERS  6372795.0f

// Create an instance of the TinyGPS objects
TinyGPS gps;

// Initialize the NewSoftSerial library to the pins you defined above
NewSoftSerial gpscom(GPSRX, GPSTX);
NewSoftSerial lcd(LCDRX, LCDTX);
NewSoftSerial rfid(RFIDRX, RFIDTX);

// This is where you create prototypes for the functions that will be used.
void getgps(TinyGPS &gps);

//Initialize serial ports and pins
void setup()
{

  //Gets communication ports up and running
  Serial.begin(9600);
  gpscom.begin(GPSBAUD);
  lcd.begin(LCDBAUD);
  rfid.begin(RFIDBAUD);
  
  //Pin definitions for speaker and RFID  
  pinMode(RESETPIN, OUTPUT);
  pinMode(pinSpeaker, OUTPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(pinSpeaker, LOW);
  
  //Initialize LCD Screen
  clue = EEPROM.read(address);
  clearLCD();
  drawboxLCD();
  backlightonLCD();
  initProgress();
  updateProgress();
  welcomeMessage();
  printClue();
}

void loop()
{
  
  clue = EEPROM.read(address);
  
  switch (clue)
  {
    case 1: 
    {
      Clue1();
      break;
    }
    case 2:
    {
      Clue2();
      break;
    }
    case 3:
    {
      Clue3();
      break;
    }
    case 4:
    {
      Clue4();
      break;
    }
    case 5:
    {
      Clue5();
      break;
    }
    case 6:
    {
      Clue6();
      break;
    }
    case 7:
    {
      Clue7();
      break;
    }
    case 8:
    {
      Clue8();
      break;
    }
    case 9:
    {
      Clue9();
      break;
    }
    case 10:
    {
      Clue10();
      break;
    }
    case 11:
    {
      Clue11();
      break;
    }
  }
}


//Distance calculation function
float DistanceBetween2Points( float Lat1, float Lon1, float Lat2, float Lon2, float unit_conversion )
  {
    float dLat = radians( Lat2 - Lat1 );
    float dLon = radians( Lon2 - Lon1 );
  
    float a = sin( dLat / 2.0f ) * sin( dLat / 2.0f ) +
            cos( radians( Lat1 ) ) * cos( radians( Lat2 ) ) *
            sin( dLon / 2.0f ) * sin( dLon / 2.0f );
            
    float d = 2.0f * atan2( sqrt( a ), sqrt( 1.0f - a ) );

    return d * EARTH_RADIUS_METERS * unit_conversion;
  }
  
//Reset ID20 function
void resetID20()
{
        digitalWrite(RESETPIN, LOW);   
        delay(100);  
}

//Tone play function
void playTone(long duration, int freq) 
{
    duration *= 1000;
    int period = (1.0 / freq) * 1000000;
    long elapsed_time = 0;
    while (elapsed_time < duration) 
    {
        digitalWrite(pinSpeaker,HIGH);
        delayMicroseconds(period / 2);
        digitalWrite(pinSpeaker, LOW);
        delayMicroseconds(period / 2);
        elapsed_time += (period);
    }
}
void toneSuccess()
{
  playTone(150, 2500);
}

void toneFailure()
{      
  playTone(150, 150); 
}

void clearLCD()
{
  lcd.print(0x7C, BYTE);
  lcd.print(0x00, BYTE);
}

void drawboxLCD()
{
  lcd.print(0x7C, BYTE);
  lcd.print(0x0C, BYTE);  
  lcd.print(0x00, BYTE);  //x1
  lcd.print(0x35, BYTE);  //y1  
  lcd.print(0x7F, BYTE);  //x2  
  lcd.print(0x35, BYTE);  //y2  
  lcd.print(0x01, BYTE); 
 
  lcd.print(0x7C, BYTE);
  lcd.print(0x0C, BYTE);  
  lcd.print(0x00, BYTE);  //x1
  lcd.print(0x3F, BYTE);  //y1  
  lcd.print(0x7F, BYTE);  //x2  
  lcd.print(0x3F, BYTE);  //y2  
  lcd.print(0x01, BYTE);  
  
  lcd.print(0x7C, BYTE);
  lcd.print(0x0C, BYTE);  
  lcd.print(0x00, BYTE);  //x1
  lcd.print(0x36, BYTE);  //y1  
  lcd.print(0x00, BYTE);  //x2  
  lcd.print(0x3E, BYTE);  //y2  
  lcd.print(0x01, BYTE); 
  
  lcd.print(0x7C, BYTE);
  lcd.print(0x0C, BYTE);  
  lcd.print(0x7F, BYTE);  //x1
  lcd.print(0x36, BYTE);  //y1  
  lcd.print(0x7F, BYTE);  //x2  
  lcd.print(0x3E, BYTE);  //y2  
  lcd.print(0x01, BYTE); 
}

void backlightonLCD()
{
  lcd.print(0x7C, BYTE);
  lcd.print(0x02, BYTE);
  lcd.print(0x63, BYTE);
}

void setTextLocation()
{
  //sets x        
  lcd.print(0x7C, BYTE);
  lcd.print(0x18, BYTE);
  lcd.print(0x00, BYTE);
  //Sets Y
  lcd.print(0x7C, BYTE);
  lcd.print(0x19, BYTE);
  lcd.print(0x31, BYTE);  
}

void updateProgress()
{
  
  clue = EEPROM.read(address);
  if(clue <= 9)
  {
  lcd.print(0x7C, BYTE);
  lcd.print(0x18, BYTE);
  lcd.print(0x5E, BYTE);
  //Sets Y
  lcd.print(0x7C, BYTE);
  lcd.print(0x19, BYTE);
  lcd.print(0x3D, BYTE);
  lcd.print("0");
    
  lcd.print(0x7C, BYTE);
  lcd.print(0x18, BYTE);
  lcd.print(0x64, BYTE);
  //Sets Y
  lcd.print(0x7C, BYTE);
  lcd.print(0x19, BYTE);
  lcd.print(0x3D, BYTE);
  lcd.print(clue);  
  }
  else
  {
  lcd.print(0x7C, BYTE);
  lcd.print(0x18, BYTE);
  lcd.print(0x5E, BYTE);
  //Sets Y
  lcd.print(0x7C, BYTE);
  lcd.print(0x19, BYTE);
  lcd.print(0x3D, BYTE);
  lcd.print(clue);
  }
}

//This function actually displays the clues. Clues have been removed for privacy reasons.  ;-)

void printClue()
{
  setTextLocation();
  switch (clue)
  {
    case 1: 
    {
      clearText();
      lcd.print("Your task is to find the key to open the  box so you can have  your ring. Follow theclues to get the key.");
      delay(12000);
      clearText();
      lcd.print("Clue 1");
      break;
    }
    case 2:
    {
      clearText();
      lcd.print("Clue 2");
      break;
    }
    case 3:
    {
      clearText();
      lcd.print("Clue 3");
      break;
    }
    case 4:
    {
      clearText();
      lcd.print("Clue 4");
      break;
    }
    case 5:
    {
      clearText();
      lcd.print("                     This is too easy for you.");
      delay(5000);
      clearText();
      lcd.print("                     Now you have to work for it a bit.");
      delay(5000);
      clearText();
      lcd.print("                     Enabling GPS");
      delay(5000);
      clearText();
      lcd.print("Clue 5");
      setDistancePosition();
      lcd.print("       miles away");
      break;
    }
    case 6:
    {
      clearText();
      lcd.print(0x7C, BYTE);
      lcd.print(0x18, BYTE);
      lcd.print(0x00, BYTE);
      lcd.print(0x7C, BYTE);
      lcd.print(0x19, BYTE);
      lcd.print(0x28, BYTE);
      lcd.print("Clue 6");
      delay(1000);
      setDistancePosition();
      lcd.print("       miles away");
      break;
    }
    case 7:
    {
      clearText();
      lcd.print(0x7C, BYTE);
      lcd.print(0x18, BYTE);
      lcd.print(0x00, BYTE);
      lcd.print(0x7C, BYTE);
      lcd.print(0x19, BYTE);
      lcd.print(0x28, BYTE);
      lcd.print("Clue 7");
      delay(1000);
      setDistancePosition();
      lcd.print("       miles away");
      break;
    }
    case 8:
    {
      clearText();
      lcd.print("Clue 8");
      delay(1000);
      setDistancePosition();
      lcd.print("       miles away");
      break;
    }
    case 9:
    {
      clearText();
        lcd.print(0x7C, BYTE);
        lcd.print(0x18, BYTE);
        lcd.print(0x00, BYTE);
        lcd.print(0x7C, BYTE);
        lcd.print(0x19, BYTE);
        lcd.print(0x28, BYTE);
      lcd.print("Clue 9");
      break;
    }
    case 10:
    {
      clearText();
        lcd.print(0x7C, BYTE);
        lcd.print(0x18, BYTE);
        lcd.print(0x00, BYTE);
        lcd.print(0x7C, BYTE);
        lcd.print(0x19, BYTE);
        lcd.print(0x28, BYTE);
      lcd.print("Clue 10");
      break;
    } 
    case 11:
    {
      clearText();
        lcd.print(0x7C, BYTE);
        lcd.print(0x18, BYTE);
        lcd.print(0x12, BYTE);
        lcd.print(0x7C, BYTE);
        lcd.print(0x19, BYTE);
        lcd.print(0x1E, BYTE);      
      lcd.print("Congratulations!");
      delay(8000);
      
      clearText();
      lcd.print("                     The key is in your   car, in the change   compartment.");
      delay(10000);
      clearText();
      
        lcd.print(0x7C, BYTE);
        lcd.print(0x18, BYTE);
        lcd.print(0x0A, BYTE);
        lcd.print(0x7C, BYTE);
        lcd.print(0x19, BYTE);
        lcd.print(0x1E, BYTE); 
        
      lcd.print("Will you marry me?");
      break;
      break;
    }  
  }
}  

void welcomeMessage()
{
  setTextLocation();
        lcd.print(0x7C, BYTE);
        lcd.print(0x18, BYTE);
        lcd.print(0x22, BYTE);
        lcd.print(0x7C, BYTE);
        lcd.print(0x19, BYTE);
        lcd.print(0x1E, BYTE);
  lcd.print("Hello Kim!");
  delay(5000);
}

void initProgress()
{
  //Sets X
  lcd.print(0x7C, BYTE);
  lcd.print(0x18, BYTE);
  lcd.print(0x04, BYTE);
  //Sets Y
  lcd.print(0x7C, BYTE);
  lcd.print(0x19, BYTE);
  lcd.print(0x3D, BYTE);
  lcd.print("Your progress: 00/11"); 
}

void clearText()
{
  lcd.print(0x7C, BYTE);
  lcd.print(0x05, BYTE);
  lcd.print(0x00, BYTE);  //x1
  lcd.print(0x00, BYTE);  //y1
  lcd.print(0x7F, BYTE);  //x2
  lcd.print(0x33, BYTE);  //y2
  setTextLocation();
}

void gpsPrint()
{
  clearText();
  setTextLocation();
  lcd.print("Miles to next clue:");
}

void setDistancePosition()
{
     lcd.print(0x7C, BYTE);
     lcd.print(0x18, BYTE);
     lcd.print(0x01, BYTE);
     //Sets Y
     lcd.print(0x7C, BYTE);
     lcd.print(0x19, BYTE);
     lcd.print(0x07, BYTE);
}

void Clue1()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "29009410E5")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}

void Clue2()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "2B00295CBB")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          setTextLocation();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}

void Clue3()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "2500AC086A")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}

void Clue4()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "2B00297812")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}


void Clue5()
{
    if(gpscom.available())     // While there is data on the RX pin...
      {
      int c = gpscom.read();    // load the data into a variable...
      if(gps.encode(c))      // if there is a new valid sentence...
        {
          float latitude, longitude;
          gps.f_get_position(&latitude, &longitude);
  
          //GPS location removed.
          float f_lat, f_lon;
          f_lat = xx.xxxx;
          f_lon = xx.xxxx;
          delay(500);
          float distance = DistanceBetween2Points(latitude, longitude, f_lat, f_lon, MILES_PER_METER );
          Serial.print("Distance: "); Serial.println(distance);
          setDistancePosition();
          lcd.print(distance);
          
          if(distance < 0.05)
             {
               toneSuccess();
               clue++;
               EEPROM.write(address, clue);
               clue = EEPROM.read(address);
               updateProgress();
               printClue();
             }      
        }
      }
}
 

void Clue6()
{
    if(gpscom.available())     // While there is data on the RX pin...
      {
      int c = gpscom.read();    // load the data into a variable...
      if(gps.encode(c))      // if there is a new valid sentence...
        {
          float latitude, longitude;
          gps.f_get_position(&latitude, &longitude);
  
          //GPS location removed.
          float f_lat, f_lon;
          f_lat = xx.xxxx;
          f_lon = xx.xxxx;
          delay(500);
          float distance = DistanceBetween2Points(latitude, longitude, f_lat, f_lon, MILES_PER_METER );
          Serial.print("Distance: "); Serial.println(distance);
          setDistancePosition();
          lcd.print(distance);
          
          if(distance < 0.05)
             {
               toneSuccess();
               clue++;
               EEPROM.write(address, clue);
               clue = EEPROM.read(address);
               updateProgress();
               printClue();
             }      
        }
      }
}


void Clue7()
{
    if(gpscom.available())     // While there is data on the RX pin...
      {
      int c = gpscom.read();    // load the data into a variable...
      if(gps.encode(c))      // if there is a new valid sentence...
        {
          float latitude, longitude;
          gps.f_get_position(&latitude, &longitude);
  
          //GPS location removed.
          float f_lat, f_lon;
          f_lat = xx.xxxx;
          f_lon = xx.xxxx;
          delay(500);
          float distance = DistanceBetween2Points(latitude, longitude, f_lat, f_lon, MILES_PER_METER );
          Serial.print("Distance: "); Serial.println(distance);
          setDistancePosition();
          lcd.print(distance);
          
          if(distance < 0.05)
             {
               toneSuccess();
               clue++;
               EEPROM.write(address, clue);
               clue = EEPROM.read(address);
               updateProgress();
               printClue();
             }      
        }
      }
}


void Clue8()
{
    if(gpscom.available())     // While there is data on the RX pin...
      {
      int c = gpscom.read();    // load the data into a variable...
      if(gps.encode(c))      // if there is a new valid sentence...
        {
          float latitude, longitude;
          gps.f_get_position(&latitude, &longitude);
  
          //GPS location removed.
          float f_lat, f_lon;
          f_lat = xx.xxxx;
          f_lon = xx.xxxx;
          delay(500);
          float distance = DistanceBetween2Points(latitude, longitude, f_lat, f_lon, MILES_PER_METER );
          Serial.print("Distance: "); Serial.println(distance);
          setDistancePosition();
          lcd.print(distance);
          
          if(distance < 0.05)
             {
               toneSuccess();
               clue++;
               EEPROM.write(address, clue);
               clue = EEPROM.read(address);
               updateProgress();
               printClue();
             }      
        }
      }
}
  

void Clue9()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "290093EB92")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          setTextLocation();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}


void Clue10()
{
      char IDstring[13];
      int  i;
      digitalWrite(RESETPIN, HIGH);
      if (rfid.available() > 0 ) 
    {
      if ( (val = rfid.read()) == 02 ) 
      {    
        delay(10);                                      
        for ( i = 0; (val = rfid.read()) != 03 ; i++) 
        {
          delay(10);
          IDstring[i] = val;
        }
        IDstring[10] = 0x00;        // tie off IDstring at the CR-LF
       Serial.println(IDstring);
        if (strcmp(IDstring, "2900941016")  == 0)  // test to see if the two strings are equal
         {
          toneSuccess();
          clue++;
          EEPROM.write(address, clue);
          clue = EEPROM.read(address);
          updateProgress();
          setTextLocation();
          printClue();
           } 
        else
        {
          toneFailure();
        }
        resetID20();                // reset after a valid read
      }
    }
}

//congratulations!
void Clue11()
{
  
}
